﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace d39
{
    public static class Shapes
    {
        public static IEnumerable<IShape> AllShapes
        {
            get
            {
                yield return new CircleShape();         //C
                yield return new SquareShape();         //S
                yield return new DiamondShape();        //D
                yield return new TriangleShape();       //T
                yield return new InverseTriangleShape();//V
                yield return new HexagonShape();        //X
                yield return new PentagonShape();       //P
                yield return new OctagonShape();        //O
                yield return new StarShape();           //E
                yield return new HeartShape();          //H
                yield return new PlusShape();           //p
                yield return new XShape();              //x
                yield return new ShieldShape();         //s
                yield return new CrescentShape();       //c
                yield return new SunShape();            //o
            }
        }
        public static IShape FromShapeChar(char ch)
        {
            return AllShapes.Where(shape => shape.ShapeChar == ch).Single();
        }
    }
    [Serializable]
    public abstract class BasicShape : IShape
    {
        public abstract char ShapeChar { get; }
        public abstract string Name { get; }
        public abstract float Size { get; }
        protected virtual float PointScale { get { return 1.0f; } }

        protected float TransformLength(float length, float pixelSize)
        {
            return length * pixelSize * PointScale * 0.5f;
        }
        protected SizeF TransformSize(SizeF size, float pixelSize)
        {
            return TransformSize(size.Width, size.Height, pixelSize);
        }
        protected SizeF TransformSize(float width, float height, float pixelSize)
        {
            return new SizeF(TransformLength(width, pixelSize), TransformLength(height, pixelSize));
        }
        protected PointF TransformPoint(float x, float y, PointF centerPixelPosition, float pixelSize)
        {
            return TransformPoint(new PointF(x, y), centerPixelPosition, pixelSize);
        }
        protected PointF TransformPoint(PointF point, PointF centerPixelPosition, float pixelSize)
        {
            return new PointF(centerPixelPosition.X + TransformLength(point.X, pixelSize), centerPixelPosition.Y + TransformLength(point.Y, pixelSize));
        }
        protected RectangleF TransformRectangle(float x, float y, float width, float height, PointF centerPixelPosition, float pixelSize)
        {
            return TransformRectangle(new PointF(x, y), new SizeF(width, height), centerPixelPosition, pixelSize);
        }
        protected RectangleF TransformRectangle(PointF position, SizeF size, PointF centerPixelPosition, float pixelSize)
        {
            return new RectangleF(TransformPoint(position, centerPixelPosition, pixelSize), TransformSize(size, pixelSize));
        }

        public abstract void Draw(Graphics graphics, Color color, bool filled, PointF centerPixelPosition, float pixelSize);
    }
    [Serializable]
    public sealed class CircleShape : BasicShape
    {
        public override char ShapeChar { get { return 'C'; } }
        public override string Name { get { return "Circle"; } }
        public override float Size { get { return 1.0f; } }
        protected override float PointScale { get { return 0.95f; } }
        public override void Draw(Graphics graphics, Color color, bool filled, PointF centerPixelPosition, float pixelSize)
        {
            var rect = TransformRectangle(-1, -1, 2, 2, centerPixelPosition, pixelSize);
            if (filled)
            {
                using (var brush = new SolidBrush(color))
                {
                    graphics.FillEllipse(brush, rect);
                }
            }
            else
            {
                using (var pen = new Pen(color))
                {
                    graphics.DrawEllipse(pen, rect);
                }
            }
        }
    }
    [Serializable]
    public abstract class PathShape : BasicShape
    {
        protected abstract GraphicsPath GetTransformedPath(PointF centerPixelPosition, float pixelSize);
        protected virtual bool DrawAsWellAsFill { get { return false; } }
        public override void Draw(Graphics graphics, Color color, bool filled, PointF centerPixelPosition, float pixelSize)
        {
            var path = GetTransformedPath(centerPixelPosition, pixelSize);
            if (filled)
            {
                using (var brush = new SolidBrush(color))
                {
                    graphics.FillPath(brush, path);
                }
            }
            if (!filled || DrawAsWellAsFill)
            {
                using (var pen = new Pen(color))
                {
                    graphics.DrawPath(pen, path);
                }
            }
        }
    }
    [Serializable]
    public abstract class PolyShape : BasicShape
    {
        protected abstract IEnumerable<PointF> Points { get; }

        protected PointF[] getTransformedPoints(PointF centerPixelPosition, float pixelSize)
        {
            return (from point in Points select TransformPoint(point, centerPixelPosition, pixelSize)).ToArray();
        }

        public override void Draw(Graphics graphics, Color color, bool filled, PointF centerPixelPosition, float pixelSize)
        {
            var points = getTransformedPoints(centerPixelPosition, pixelSize);
            if (filled)
            {
                using (var brush = new SolidBrush(color))
                {
                    graphics.FillPolygon(brush, points);
                }
            }
            else
            {
                using (var pen = new Pen(color))
                {
                    graphics.DrawPolygon(pen, points);
                }
            }
        }
    }
    [Serializable]
    public sealed class SquareShape : PolyShape
    {
        public override char ShapeChar { get { return 'S'; } }
        public override string Name { get { return "Square"; } }
        public override float Size { get { return 0.9f; } }
        protected override IEnumerable<PointF> Points { get { return points; } }
        private static PointF[] points = new[] {
            new PointF(0.8f, 0.8f),
            new PointF(0.8f, -0.8f),
            new PointF(-0.8f, -0.8f),
            new PointF(-0.8f, 0.8f),
        };
    }
    [Serializable]
    public sealed class TriangleShape : PolyShape
    {
        public override char ShapeChar { get { return 'T'; } }
        public override string Name { get { return "Triangle"; } }
        public override float Size { get { return 0.7f; } }
        protected override IEnumerable<PointF> Points { get { return points; } }
        private static PointF[] points = new[] {
            new PointF(0, -1),
            new PointF(1, 0.75f),
            new PointF(-1, 0.75f),
        };
    }
    [Serializable]
    public sealed class InverseTriangleShape : PolyShape
    {
        public override char ShapeChar { get { return 'V'; } }
        public override string Name { get { return "Inverse Triangle"; } }
        public override float Size { get { return 0.7f; } }
        protected override IEnumerable<PointF> Points { get { return points; } }
        private static PointF[] points = new[] {
            new PointF(0, 1),
            new PointF(1, -0.75f),
            new PointF(-1, -0.75f),
        };
    }
    [Serializable]
    public sealed class DiamondShape : PolyShape
    {
        public override char ShapeChar { get { return 'D'; } }
        public override string Name { get { return "Diamond"; } }
        public override float Size { get { return 0.8f; } }
        protected override float PointScale { get { return 1.1f; } }
        protected override IEnumerable<PointF> Points { get { return points; } }
        private static PointF[] points = new[] {
            new PointF(0, 1f),
            new PointF(1f, 0),
            new PointF(0, -1f),
            new PointF(-1f, 0),
        };
    }
    [Serializable]
    public sealed class PentagonShape : PolyShape
    {
        public override char ShapeChar { get { return 'P'; } }
        public override string Name { get { return "Pentagon"; } }
        public override float Size { get { return 0.8f; } }
        protected override IEnumerable<PointF> Points { get { return points; } }
        private static PointF[] points = new[] {
            new PointF(0, -1),
            new PointF(1, -0.25f),
            new PointF(0.65f, 1),
            new PointF(-0.65f, 1),
            new PointF(-1, -0.25f),
        };
    }

    [Serializable]
    public sealed class HexagonShape : PolyShape
    {
        public override char ShapeChar { get { return 'X'; } }
        public override string Name { get { return "Hexagon"; } }
        public override float Size { get { return 0.8f; } }
        protected override IEnumerable<PointF> Points { get { return points; } }
        protected override float PointScale { get { return 1.1f; } }
        private static PointF[] points = new[] {
            new PointF(-0.4f, -0.8f),
            new PointF(0.4f, -0.8f),
            new PointF(0.9f, 0),
            new PointF(0.4f, 0.8f),
            new PointF(-0.4f, 0.8f),
            new PointF(-0.9f, 0),
        };
    }

    [Serializable]
    public sealed class OctagonShape : PolyShape
    {
        public override char ShapeChar { get { return 'O'; } }
        public override string Name { get { return "Octagon"; } }
        public override float Size { get { return 0.8f; } }
        protected override IEnumerable<PointF> Points { get { return points; } }
        protected override float PointScale { get { return 0.9f; } }
        private static PointF[] points = new[] {
            new PointF(-0.4f, -1),
            new PointF(0.4f, -1),
            new PointF(1, -0.4f),
            new PointF(1, 0.4f),
            new PointF(0.4f, 1),
            new PointF(-0.4f, 1),
            new PointF(-1, 0.4f),
            new PointF(-1, -0.4f),
        };
    }

    [Serializable]
    public sealed class HeartShape : PathShape
    {
        public override char ShapeChar { get { return 'H'; } }
        public override string Name { get { return "Heart"; } }
        public override float Size { get { return 0.8f; } }
        protected override float PointScale { get { return 0.6f; } }
        protected override bool DrawAsWellAsFill { get { return true; } }
        protected override GraphicsPath GetTransformedPath(PointF centerPixelPosition, float pixelSize)
        {
            var path = new GraphicsPath();
            path.AddArc(TransformRectangle(-1.4f, -1.39f, 1.5f, 1.5f, centerPixelPosition, pixelSize), 135, 195);
            path.AddArc(TransformRectangle(-0.1f, -1.39f, 1.5f, 1.5f, centerPixelPosition, pixelSize), 210, 195);
            path.AddLines(new[] {
                TransformPoint(1.17f, 0, centerPixelPosition, pixelSize),
                TransformPoint(0, 1.31f, centerPixelPosition, pixelSize),
                TransformPoint(-1.17f, 0, centerPixelPosition, pixelSize),
            });
            return path;
        }
    }

    [Serializable]
    public sealed class StarShape : PolyShape
    {
        public override char ShapeChar { get { return 'E'; } }
        public override string Name { get { return "Star"; } }
        public override float Size { get { return 0.9f; } }
        protected override float PointScale { get { return 1.2f; } }
        protected override IEnumerable<PointF> Points { get { return points; } }
        private static PointF[] points = new[] {
            new PointF(0, -1),
            new PointF(0.25f, -0.25f),
            new PointF(1, -0.25f),
            new PointF(0.4f, 0.2f),
            new PointF(0.65f, 1),
            new PointF(0, 0.5f),
            new PointF(-0.65f, 1),
            new PointF(-0.4f, 0.2f),
            new PointF(-1, -0.25f),
            new PointF(-0.25f, -0.25f)
        };
    }
    [Serializable]
    public sealed class PlusShape : PolyShape
    {
        public override char ShapeChar { get { return 'p'; } }
        public override string Name { get { return "Cross (+)"; } }
        public override float Size { get { return 0.6f; } }
        protected override IEnumerable<PointF> Points { get { return points; } }
        private static PointF[] points = new[] {
            new PointF(-0.35f, -1),
            new PointF(0.35f, -1),
            new PointF(0.35f, -0.35f),
            new PointF(1, -0.35f),
            new PointF(1, 0.35f),
            new PointF(0.35f, 0.35f),
            new PointF(0.35f, 1),
            new PointF(-0.35f, 1),
            new PointF(-0.35f, 0.35f),
            new PointF(-1, 0.35f),
            new PointF(-1, -0.35f),
            new PointF(-0.35f, -0.35f),
        };
    }
    [Serializable]
    public sealed class XShape : PolyShape
    {
        public override char ShapeChar { get { return 'x'; } }
        public override string Name { get { return "Cross (X)"; } }
        public override float Size { get { return 0.6f; } }
        protected override IEnumerable<PointF> Points { get { return points; } }
        private static PointF[] points = new[] {
            new PointF(0.5f, -1),
            new PointF(1, -0.5f),
            new PointF(0.5f, 0),
            new PointF(1, 0.5f),
            new PointF(0.5f, 1),
            new PointF(0, 0.5f),
            new PointF(-0.5f, 1),
            new PointF(-1, 0.5f),
            new PointF(-0.5f, 0),
            new PointF(-1, -0.5f),
            new PointF(-0.5f, -1),
            new PointF(0, -0.5f),
        };
    }

    [Serializable]
    public sealed class ShieldShape : PathShape
    {
        public override string Name { get { return "Shield"; } }
        public override char ShapeChar { get { return 's'; } }
        public override float Size { get { return 0.8f; } }
        private static readonly float threeOverRoot5 = 3.0f / (float)Math.Sqrt(5);
        private static readonly float root2 = (float)Math.Sqrt(2);
        protected override GraphicsPath GetTransformedPath(PointF centerPixelPosition, float pixelSize)
        {
            var path = new GraphicsPath();
            path.AddArc(TransformRectangle(-5, -threeOverRoot5 * 2 - 1, 6, threeOverRoot5 * 4, centerPixelPosition, pixelSize), 0, 45);
            path.AddArc(TransformRectangle(-1, -threeOverRoot5 * 2 - 1, 6, threeOverRoot5 * 4, centerPixelPosition, pixelSize), 135, 45);
            path.AddArc(TransformRectangle(-root2 / 2 - 0.5f, -root2 / 2 - 1.5f, root2, root2, centerPixelPosition, pixelSize), 135, -90);
            path.AddArc(TransformRectangle(-root2 / 2 + 0.5f, -root2 / 2 - 1.5f, root2, root2, centerPixelPosition, pixelSize), 135, -90);
            return path;
        }
    }
    [Serializable]
    public sealed class CrescentShape : PathShape
    {
        public override string Name { get { return "Crescent"; } }
        public override char ShapeChar { get { return 'c'; } }
        public override float Size { get { return 0.6f; } }
        private static readonly float root2 = (float)Math.Sqrt(2);
        protected override GraphicsPath GetTransformedPath(PointF centerPixelPosition, float pixelSize)
        {
            var path = new GraphicsPath();
            path.AddArc(TransformRectangle(-1, -1, 2, 2, centerPixelPosition, pixelSize), 45, 270);
            path.AddArc(TransformRectangle(0, -root2 / 2, root2, root2, centerPixelPosition, pixelSize), -90, -180);
            return path;
        }
    }
    [Serializable]
    public sealed class SunShape : PolyShape
    {
        private const double outerRad = 1.05;
        private const double innerRad = 0.75;
        private const int numPoints = 10;
        public override string Name { get { return "Sun"; } }
        public override char ShapeChar { get { return 'o'; } }
        public override float Size { get { return 0.75f; } }
        private static List<PointF> points = GetPoints().ToList();
        protected override IEnumerable<PointF> Points { get { return points; } }

        private static IEnumerable<PointF> GetPoints()
        {
            for (var i = 0; i < numPoints; i++)
            {
                yield return new PointF((float)(outerRad * Math.Sin(i * 2 * Math.PI / numPoints)), (float)(outerRad * Math.Cos(i * 2 * Math.PI / numPoints)));
                yield return new PointF((float)(innerRad * Math.Sin((i * 2 + 1) * Math.PI / numPoints)), (float)(innerRad * Math.Cos((i * 2 + 1) * Math.PI / numPoints)));
            }
        }

    }
}